package com.lxw.report.meta.config.shiro;

import org.apache.shiro.session.Session;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
import org.springframework.data.redis.core.RedisTemplate;

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

/**
 * @author lxw
 * @time 2022/3/7 13:55
 */

public class RedisSessionDAO extends EnterpriseCacheSessionDAO {
    /**
     * 存入Redis中的SessionID的前缀
     */
    private static final String PREFIX = "SHIRO_SESSION_ID";
    /**
     * 有效期（后续使用时会增加时间单位，秒） 1天
     */
    private static final int EXPRIE = 86400;
    /**
     * Redis 操作工具
     */
    private RedisTemplate<Object, Object> redisTemplate;

    /**
     * 构造函数
     */
    public RedisSessionDAO(RedisTemplate<Object, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * shiro创建session时，将session保存到redis
     * @param session
     * @return
     */
    @Override
    protected Serializable doCreate(Session session) {
        //生成SessionID
        Serializable sessionId = super.doCreate(session);
        //将sessionid作为Key，session作为value存入redis
        redisTemplate.opsForValue().set(PREFIX+sessionId, session,EXPRIE,TimeUnit.SECONDS);
        return sessionId;
    }

    /**
     * 当用户维持会话时，刷新session的有效时间
     * @param session
     */
    @Override
    protected void doUpdate(Session session) {
        super.doUpdate(session);
        //将sessionid作为Key，session作为value存入redis，并设置有效期
        redisTemplate.opsForValue().set(PREFIX+session.getId(), session, EXPRIE, TimeUnit.SECONDS);
    }

    /**
     * 当用户注销或会话过期时，将session从redis中删除
     * @param session
     */
    @Override
    protected void doDelete(Session session) {
        super.doDelete(session);
        //null 验证
        if (session == null) {
            return;
        }
        //从Redis中删除指定SessionId的k-v
        redisTemplate.delete(PREFIX+session.getId());
    }

    /**
     *  shiro通过sessionId获取Session对象，从redis中获取
     * @param sessionId
     * @return
     */
    @Override
    protected Session doReadSession(Serializable sessionId) {
        if (sessionId == null) {
            return null;
        }
        // 先从缓存中获取session，如果没有再去数据库中获取
        Session session = super.doReadSession(sessionId);
        if(session==null){
            //从Redis中读取Session对象, 出错（可能修改属性导致序列化错误）则删除缓存会话
            try{
                session = (Session)redisTemplate.opsForValue().get(PREFIX+sessionId);
            }catch (Exception e){
                //从Redis中删除指定SessionId的k-v
                redisTemplate.delete(PREFIX+sessionId);
            }
        }
        return session;
    }
}

